import java.util.ArrayList;
import java.util.List;

/**
 * 给定一个只包含数字的字符串，复原它并返回所有可能的 IP 地址格式。
 * <p>
 * 有效的 IP 地址 正好由四个整数（每个整数位于 0 到 255 之间组成，且不能含有前导 0），整数之间用 '.' 分隔。
 * <p>
 * 例如："0.1.2.201" 和 "192.168.1.1" 是 有效的 IP 地址，但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效的 IP 地址。
 * <p>
 *  
 * <p>
 * 示例 1：
 * <p>
 * 输入：s = "25525511135"
 * 输出：["255.255.11.135","255.255.111.35"]
 * 示例 2：
 * <p>
 * 输入：s = "0000"
 * 输出：["0.0.0.0"]
 * 示例 3：
 * <p>
 * 输入：s = "1111"
 * 输出：["1.1.1.1"]
 * 示例 4：
 * <p>
 * 输入：s = "010010"
 * 输出：["0.10.0.10","0.100.1.0"]
 * 示例 5：
 * <p>
 * 输入：s = "101023"
 * 输出：["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
 *  
 * <p>
 * 提示：
 * <p>
 * 0 <= s.length <= 3000
 * s 仅由数字组成
 */
class Solution {

    public static void main(String[] args) {
        System.out.println(restoreIpAddresses("25525511135"));
        System.out.println(restoreIpAddresses("0000"));
        System.out.println(restoreIpAddresses("1111"));
        System.out.println(restoreIpAddresses("010010"));
        System.out.println(restoreIpAddresses("101023"));
    }

    public static List<String> res;

    /**
     * 分隔的规则有：
     * 除了0之外，不能用0开头
     * 不能大于255
     *
     * @param s
     * @return
     */
    public static List<String> restoreIpAddresses(String s) {
        res = new ArrayList<>();
        if (s.length() >= 4 && s.length() <= 12) {
            // 在合法的情况下，需要遍历处理
            loop(s, 0, 0, 1);
        }
        return res;
    }

    /**
     * 循环处理，由于创建字符串开销太大，官方解法中使用数组来存储位置+回溯来存储前两位的结果
     *
     * @param s       字符串
     * @param oneUsed 第一段已经使用的长度
     * @param twoUsed 第二段已经使用的长度
     * @param pos     当前是地址的第几段
     */
    public static void loop(String s, int oneUsed, int twoUsed, int pos) {
        // 找到当前段，可以使用几个字符，范围是1-3
        int left = s.length() - (oneUsed + twoUsed);
        int max = Math.min(left - (4 - pos), 3);
        int min = Math.max(left - 3 * (4 - pos), 1);
        if (pos == 1) {
            for (int i = min; i <= max; i++) {
                String tmp = s.substring(0, i);
                if ((!tmp.startsWith("0") && Integer.parseInt(tmp) <= 255) || (tmp.startsWith("0") && "0".equals(tmp))) {
                    loop(s, i, 0, pos + 1);
                }
            }
        } else if (pos == 2) {
            for (int i = min; i <= max; i++) {
                String tmp = s.substring(oneUsed, oneUsed + i);
                if ((!tmp.startsWith("0") && Integer.parseInt(tmp) <= 255) || (tmp.startsWith("0") && "0".equals(tmp))) {
                    loop(s, oneUsed, i, pos + 1);
                }
            }
        } else if (pos == 3) {
            for (int i = min; i <= max; i++) {
                String thirdValue = s.substring(oneUsed + twoUsed, oneUsed + twoUsed + i);
                String fourValue = s.substring(oneUsed + twoUsed + i);
                if ((!thirdValue.startsWith("0") && Integer.parseInt(thirdValue) <= 255) || (thirdValue.startsWith("0") && "0".equals(thirdValue))) {
                    if ((!fourValue.startsWith("0") && Integer.parseInt(fourValue) <= 255) || (fourValue.startsWith("0") && "0".equals(fourValue))) {
                        res.add(s.substring(0, oneUsed) + "."
                                + s.substring(oneUsed, oneUsed + twoUsed) + "."
                                + thirdValue + "."
                                + fourValue
                        );
                    }
                }
            }
        }
    }

    /**
     * 分隔的规则有：
     * 除了0之外，不能用0开头
     * 不能大于255
     *
     * @param s
     * @return
     */
    public static List<String> restoreIpAddresses2(String s) {
        res = new ArrayList<>();
        char[] chars = s.toCharArray();
        if (chars.length >= 4 && chars.length <= 12) {
            // 在合法的情况下，需要遍历处理
            loop2(chars, "", "", 1);
        }
        return res;
    }

    /**
     * 循环处理
     *
     * @param chars    字符串
     * @param oneValue 第一段
     * @param twoValue 第二段
     * @param pos      当前是地址的第几段
     */
    public static void loop2(char[] chars, String oneValue, String twoValue, int pos) {
        int oneUsed = oneValue.length();
        int twoUsed = twoValue.length();
        // 找到当前段，可以使用几个字符，范围是1-3
        int used = oneUsed + twoUsed;
        int left = chars.length - used;
        int max = Math.min(left - (4 - pos), 3);
        int min = Math.max(left - 3 * (4 - pos), 1);
        if (pos == 1) {
            for (int i = min; i <= max; i++) {
                int value = 0;
                for (int j = i - 1, x = 1; j >= 0; j--, x *= 10) {
                    value += x * (chars[j] - '0');
                }
                if ((chars[0] != '0' && value <= 255) || (chars[0] == '0' && i == 1)) {
                    loop2(chars, value + "", "", pos + 1);
                }
            }
        } else if (pos == 2) {
            for (int i = min; i <= max; i++) {
                int value = 0;
                for (int j = oneUsed + i - 1, x = 1; j >= oneUsed; j--, x *= 10) {
                    value += x * (chars[j] - '0');
                }
                if ((chars[oneUsed] != '0' && value <= 255) || (chars[oneUsed] == '0' && i == 1)) {
                    loop2(chars, oneValue, value + "", pos + 1);
                }
            }
        } else if (pos == 3) {
            for (int i = min; i <= max; i++) {
                int thirdValue = 0;
                for (int j = used + i - 1, x = 1; j >= used; j--, x *= 10) {
                    thirdValue += x * (chars[j] - '0');
                }
                int fourValue = 0;
                for (int j = chars.length - 1, x = 1; j >= used + i; j--, x *= 10) {
                    fourValue += x * (chars[j] - '0');
                }
                if ((chars[used] != '0' && thirdValue <= 255) || (chars[used] == '0' && i == 1)) {
                    if ((chars[used + i] != '0' && fourValue <= 255) || (chars[used + i] == '0' && left - i == 1)) {
                        res.add(oneValue + "." + twoValue + "." + thirdValue + "." + fourValue);
                    }
                }
            }
        }
    }
}

/**
 * 官方解法
 */
class Solution2 {
    static final int SEG_COUNT = 4;
    List<String> ans = new ArrayList<String>();
    int[] segments = new int[SEG_COUNT];

    public List<String> restoreIpAddresses(String s) {
        segments = new int[SEG_COUNT];
        dfs(s, 0, 0);
        return ans;
    }

    public void dfs(String s, int segId, int segStart) {
        // 如果找到了 4 段 IP 地址并且遍历完了字符串，那么就是一种答案
        if (segId == SEG_COUNT) {
            if (segStart == s.length()) {
                StringBuffer ipAddr = new StringBuffer();
                for (int i = 0; i < SEG_COUNT; ++i) {
                    ipAddr.append(segments[i]);
                    if (i != SEG_COUNT - 1) {
                        ipAddr.append('.');
                    }
                }
                ans.add(ipAddr.toString());
            }
            return;
        }

        // 如果还没有找到 4 段 IP 地址就已经遍历完了字符串，那么提前回溯
        if (segStart == s.length()) {
            return;
        }

        // 由于不能有前导零，如果当前数字为 0，那么这一段 IP 地址只能为 0
        if (s.charAt(segStart) == '0') {
            segments[segId] = 0;
            dfs(s, segId + 1, segStart + 1);
        }

        // 一般情况，枚举每一种可能性并递归
        int addr = 0;
        for (int segEnd = segStart; segEnd < s.length(); ++segEnd) {
            addr = addr * 10 + (s.charAt(segEnd) - '0');
            if (addr > 0 && addr <= 0xFF) {
                segments[segId] = addr;
                dfs(s, segId + 1, segEnd + 1);
            } else {
                break;
            }
        }
    }
}
